package asteroids.model;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.*;
import org.antlr.v4.runtime.RecognitionException;

import asteroids.model.programs.parsing.*;
import asteroids.model.programs.*;
import asteroids.model.programs.expression.*;
import asteroids.model.programs.statement.*;
import asteroids.model.programs.type.*;
import asteroids.*;

/**
 * @author Martin & Anton
 *
 */
public class Facade implements IFacade<World, Ship, Asteroid, Bullet, Program>{
	/**
	 * Default constructor
	 */
	public Facade(){
	}
	
	/**
	 * A method to parse a program from a given string.
	 * @param text
	 * @return ParseOutcome<Program>
	 */
	public ParseOutcome<Program> parseProgram(String text){
		ProgramFactoryImpl factory = new ProgramFactoryImpl();
	    ProgramParser<E, S, T> parser = new ProgramParser<>(factory);
	    try {
	        parser.parse(text);
	        List<String> errors = parser.getErrors();
	        if(! errors.isEmpty()) {
	          return ParseOutcome.failure(errors.get(0));
	        } else {
	          return ParseOutcome.success(new Program(parser.getGlobals(), parser.getStatement())); 
	        }
	    } catch(RecognitionException e) {
	      return ParseOutcome.failure(e.getMessage());
	    }
	}
	
	/**
	 * A method to load a program from a given stream.
	 * @param stream
	 * @return ParseOutcome<Program>
	 * @throws IOException 
	 */
	@Override
	public ParseOutcome<Program> loadProgramFromStream(
			InputStream stream) throws IOException {
		return parseProgram(slurp(stream, 1000));
	}

	/**
	 * A method to load a program from a given URL.
	 * @param url
	 * @return ParseOutcome<Program>
	 * @throws IOException 
	 */
	@Override
	public ParseOutcome<Program> loadProgramFromUrl(URL url)
			throws IOException {
		return loadProgramFromStream((InputStream) url.getContent());
	}

	/**
	 * Returns whether type checking is supported.
	 * @return true
	 */
	@Override
	public boolean isTypeCheckingSupported() {
		return true;
	}

	/**
	 * Typecheck a program.
	 * @param program
	 * @return If the typecheck wasn't successful, this method returns failed TypeCheckOutcome.
	 * 			| return TypeCheckOutcome.failure(typecheck)
	 * @return If the typecheck was successful, this method returns a successful TypeCheckOutcome.
	 * 			| return TypeCheckOutcome.success()
	 * 
	 */
	@Override
	public TypeCheckOutcome typeCheckProgram(Program program) {
		String typecheck = program.typecheck();
		if(!typecheck.equals("")){
			return TypeCheckOutcome.failure(typecheck);
		}
		return TypeCheckOutcome.success();
	}

	/**
	 * Set a program to a ship.
	 * @param Give a program to a ship.
	 */
	@Override
	public void setShipProgram(Ship ship, Program program) {
		try{
			ship.setProgram(program);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @setProgram(ship, program)");
		}
	}
	/**
	 * Method to create a World
	 * @param width
	 * @param height
	 * @return the new world
	 * @throws ModelException
	 * 			When an IllegelArgumentException is caught.
	 */
	public World createWorld(double width, double height){
		try{
			World world = new World(width, height);
			return world;
		}
		catch (IllegalArgumentException e){
			throw new ModelException("Invalid Arguments @createWorld()");
		}
	}

	/**
	 * @param world
	 * @effect world.getWidth()
	 * @throws ModelException
	 * 			| world == null
	 */
	public double getWorldWidth(World world){
		  try{
			  return world.getWidth();
		  }
		  catch (NullPointerException e){
			  throw new ModelException("Null object @getWorldWidth()");
		  }
	  }

	/**
	 * @param world
	 * @effect world.getHeight()
	 * @throws ModelException
	 * 			| world == null
	 */
	public double getWorldHeight(World world){
		try{
			return world.getHeight();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getWorldHeight()");
		}
	  }
	
	/**
	 * @param world
	 * @effect world.getShips()
	 * @throws ModelException
	 * 			| world == null
	 */
	public Set<Ship> getShips(World world){
		try{
			return world.getShips();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShips(world)");
		  }
	}
	
	/**
	 * @param world
	 * @effect world.getAsteroids()
	 * @throws ModelException
	 * 			| world == null
	 */
	public Set<Asteroid> getAsteroids(World world){
		try{
			return world.getAsteroids();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getAsteroids(world)");
		  }
	}
	
	/**
	 * @param world
	 * @effect world.getBullets()
	 * @throws ModelException
	 * 			| world == null
	 */
	public Set<Bullet> getBullets(World world){
		try{
			return world.getBullets();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBullets(world)");
		}
	}
	
	/**
	 * @param world
	 * @param ship
	 * @effect world.addSpaceObject(ship)
	 * @throws ModelException
	 * 			| if(world == null || ship == null)
	 * 			| if an IllegalArgumentException is caught.
	 * 			| if an IllegalStateException is caught.
	 */
	public void addShip(World world, Ship ship){
		try{
			world.addSpaceObject(ship);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @addShip(world, ship)");
		}
		catch (IllegalArgumentException e){
			throw new ModelException("IllegalArgument @addShip(world, ship)");
		}
		catch (IllegalStateException e){
			throw new ModelException("IllegalState @addShip(world, ship)");
		}
	}
	
	/**
	 * @param world
	 * @param asteroid
	 * @effect	world.addSpaceObject(asteroid)
	 * @throws ModelException
	 * 			| if(world == null || asteroid == null)
	 * 			| if an IllegalArgumentException is caught.
	 * 			| if an IllegalStateException is caught.
	 */
	public void addAsteroid(World world, Asteroid asteroid){
		try{
			world.addSpaceObject(asteroid);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @addAsteroid(world, asteroid)");
		}
		catch (IllegalArgumentException e){
			throw new ModelException("IllegalArgument @addAsteroid(world, asteroid)");
		}
		catch (IllegalStateException e){
			throw new ModelException("IllegalState @addShip(world, ship)");
		}
	}
	
	/**
	 * @param world
	 * @param ship
	 * @effect world.removeSpaceObject(ship)
	 * @throws ModelException
	 * 			| if(world == null || ship == null)
	 * 			| if an IllegalArgumentException is caught.
	 */
	public void removeShip(World world, Ship ship){
		try{
			world.removeSpaceObject(ship);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @removeShip(world, ship)");
		}
		catch (IllegalArgumentException e){
			throw new ModelException("IllegalArgument @removeShip(world, ship)");
		}
	}
	
	/**
	 * @param world
	 * @param asteroid
	 * @effect world.removeSpaceObject(asteroid)
	 * @throws ModelException
	 * 			| if(world == null || asteroid == null)
	 * 			| if an IllegalArgumentException is caught.
	 */
	public void removeAsteroid(World world, Asteroid asteroid){
		try{
			world.removeSpaceObject(asteroid);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @removeAsteroid(world, asteroid)");
		}
		catch (IllegalArgumentException e){
			throw new ModelException("IllegalArgument @removeAsteroid(world, asteroid)");
		}
	}
	
	/**
	 * Method to create a ship
	 * @return the new ship
	 * @throws ModelException
	 * 			When an IllegalArgumentException is caught.
	 */
	public Ship createShip() throws ModelException{
		try{
		Ship ship = new Ship();
		return ship;
		}
		catch (IllegalArgumentException e){
			throw new ModelException("Invalid Arguments @createShip()");
		}
	}

	/**
	 * Create a ship with a given position, speed, radius and angle.
	 * @param x
	 * @param y
	 * @param speed_x
	 * @param speed_y
	 * @param radius
	 * @param angle
	 * @param mass
	 * @throws ModelException
	 * 			When an IllegalArgumentException is caught.
	 */
	public Ship createShip(double x, double y, double xVelocity, double yVelocity, double radius, double angle, double mass) throws ModelException{
		try{
			Ship ship = new Ship(x,y,xVelocity,yVelocity,radius,angle, mass);
			return ship;
			}
		catch (IllegalArgumentException e){
			throw new ModelException("Invalid Arguments @createShip(x,y,xV,yV,rad,angle)");
		}
	}
	
	
	
	/**
	 * @param ship
	 * @return ship.isThrusterActive()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public boolean isShipThrusterActive(Ship ship){
		try{
			return ship.isThrusterActive();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @isShipThrusterActive(ship)");
		}
	}

	/**
	 * @param ship
	 * @param active
	 * @effect	ship.setThruster(active)
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public void setThrusterActive(Ship ship, boolean active){
		try{
			ship.setThruster(active);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @setThrusterActive(ship, active)");
		}
		catch (IllegalStateException e){
			throw new ModelException("Illegal State @setThrusterActive(ship, active)");
		}
	}
	
	/**
	 * Create an asteroid with a given position, speed, radius and angle.
	 * @param x
	 * @param y
	 * @param speed_x
	 * @param speed_y
	 * @param radius
	 * @throws ModelException
	 * 			When an IllegalArgumentException is caught.
	 */
	public Asteroid createAsteroid(double x, double y, double xVelocity, double yVelocity, double radius) throws ModelException{
		try{
			return createAsteroid(x, y, xVelocity, yVelocity, radius, new Random());
			}
		catch (IllegalArgumentException e){
			throw new ModelException("Invalid Arguments @createAsteroid(x,y,xV,yV,rad)");
		}
	}
	
	/**
	 * Create an asteroid with a given position, speed, radius and angle.
	 * @param x
	 * @param y
	 * @param speed_x
	 * @param speed_y
	 * @param radius
	 * @param random
	 * @throws ModelException
	 * 			When an IllegalArgumentException is caught.
	 */
	public Asteroid createAsteroid(double x, double y, double xVelocity, double yVelocity, double radius, Random random) throws ModelException{
		try{
			Asteroid asteroid = new Asteroid(x,y,xVelocity,yVelocity,radius, random);
			return asteroid;
			}
		catch (IllegalArgumentException e){
			throw new ModelException("Invalid Arguments @createAsteroid(x,y,xV,yV,rad)");
		}
	}
	
	
	
	/**
	 * Move a ship to his new position after a given time dt.
	 * @param ship
	 * @param dt
	 * @effect ship.move(dt)
	 * @throws ModelException
	 * 			When an IllegalArgumentException is caught.
	 */
	public void move(Ship ship, double dt) throws ModelException{
		try{
			ship.move(dt);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @move()");
		}
		catch (IllegalArgumentException e){
			throw new ModelException("Illegal argument @move(" + ship + "," + dt + ")");
		}
	}
	
	/**
	 * Change the velocity of a ship with a certain amount.
	 * @param ship
	 * @param amount
	 * @effect ship.thrust(amount)
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public void thrust(Ship ship, double amount) throws ModelException{
		try{
			ship.thrust(amount);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @thrust()");
		}
	}
	
	/**
	 * Turn a ship with a certain angle.
	 * @param ship
	 * @param angle
	 * @effect ship.turn(angle)
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public void turn(Ship ship, double angle) throws ModelException{
		try{
			ship.turn(angle);
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @turn()");
		}
	}
	
	/**
	 * Get the distance between two ships (= the distance one ship has to move, before touching the other).
	 * @param ship1
	 * @param ship2
	 * @return ship1.getDistanceTo(ship2)
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship1 == null || ship2 == null
	 * @throws ModelException
	 * 			When an object is in an invalid state.
	 * 			| !ship1.isAlive() || !ship2.isAlive()
	 */
	public double getDistanceBetween(Ship ship1, Ship ship2) throws ModelException{
		try{
			return ship1.getDistanceTo(ship2);
		}
		catch (ModelException e){
			throw new ModelException("Null object @getDistanceBetween()");
		}
		catch (IllegalStateException e) {
			throw new ModelException("Invalid State @getDistanceBetween()");
		}
	}
	
	/**
	 * Returns if the two given ships overlap.
	 * @param ship1
	 * @param ship2
	 * @return ship1.overlapsWith(ship2)
	 * @throws ModelException
	 * 			If a null object is given
	 * 			| ship1 == null || ship2 == null
	 * @throws ModelException
	 * 			When an object is in an invalid state.
	 * 			| !ship1.isAlive() || !ship2.isAlive()
	 */
	public boolean overlap(Ship ship1, Ship ship2) throws ModelException{
		try{
			return ship1.overlapsWith(ship2);
		}
		catch (ModelException e){
			throw new ModelException("Null object @overlap()");
		}
		catch (IllegalStateException e) {
			throw new ModelException("Invalid State @overlapsWith()");
		}
	}
	
	/**
	 * This method gives the time till the collision of the 2 ships.
	 * Returns Double.POSITIVE_INFINITY if the ships will never collide, or if the ships are the same.
	 * @param	ship1
	 * @param	ship2
	 * @effect	ship1.getTimeToCollision(ship2)
	 * @throws	ModelExcpetion
	 * 			If one or both of the ships are null.
	 * 			| If(ship1==null || ship2==null)
	 * @throws ModelException
	 * 			When an object is in an invalid state.
	 * 			| !ship1.isAlive() || !ship2.isAlive()
	 */
	public double getTimeToCollision(Ship ship1, Ship ship2) throws ModelException {
		try {
			return ship1.getTimeToCollision(ship2);
		}
		catch(NullPointerException e) {
			throw new ModelException("Null object @getTimeToCollision()");
		}
		catch (IllegalStateException e) {
			throw new ModelException("Invalid State @getTimeToCollision()");
		}
	}
	
	/**
	 * This will give the point where the two ships will collide, if they will collide.
	 * @param ship1
	 * @param ship2
	 * @return	ship1.getCollisionPosition(ship2)
	 * @throws	ModelExcpetion
	 * 			If one or both of the ships is null.
	 * 			| if(ship1==null || ship2==null)
	 * @throws ModelException
	 * 			When an object is in an invalid state.
	 * 			| !ship1.isAlive() || !ship2.isAlive()
	 */
	public double[] getCollisionPosition(Ship ship1, Ship ship2) throws ModelException {
		try{
			return ship1.getCollisionPosition(ship2);
		}
		catch(ModelException e) {
			throw new ModelException("Null object @getCollisionPosition()");
		}
		catch (IllegalStateException e) {
			throw new ModelException("Invalid State @getCOllisionPosition()");
		}
	}
	
	/**
	 * @param o
	 * @return result
	 * 			|result =  o instanceof Ship
	 */
	public boolean isShip(Object o){
		return (o instanceof Ship);
	}
	
	/**
	 * Get the X-position of a ship.
	 * @param ship
	 * @return ship.getX()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public double getShipX(Ship ship) throws ModelException{
		try{
			return ship.getX();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShipX()");
		}
	}
	
	/**
	 * Get the Y-position of a ship.
	 * @param ship
	 * @return ship.getY()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public double getShipY(Ship ship) throws ModelException{
		try{
			return ship.getY();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShipY()");
		}
	}
	
	/**
	 * Get the X-velocity of a ship.
	 * @param ship
	 * @return shp.getXVelocity()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public double getShipXVelocity(Ship ship) throws ModelException{
		try{
			return ship.getXVelocity();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShipXVelocity()");
		}
	}
	
	/**
	 * Get the Y-velocity of a ship.
	 * @param ship
	 * @return ship.getYVelocity()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public double getShipYVelocity(Ship ship) throws ModelException{
		try{
			return ship.getYVelocity();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShipYVelocity()");
		}
	}
	
	/**
	 * Get the radius of a ship.
	 * @param ship
	 * @return ship.getRadius()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public double getShipRadius(Ship ship) throws ModelException{
		try{
			return ship.getRadius();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShipRadius()");
		}
	}
	
	/**
	 * Get the direction of a ship.
	 * @param ship
	 * @return ship.getDirection()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public double getShipDirection(Ship ship) throws ModelException{
		try{
			return ship.getDirection();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShipDirection()");
		}
	}
	
	/**
	 * Get the mass of a ship.
	 * @param ship
	 * @return ship.getMass()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public double getShipMass(Ship ship) throws ModelException{
		try{
			return ship.getMass();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShipMass()");
		}
	}
	
	/**
	 * Get the world of a ship.
	 * @param ship
	 * @return ship.getWorld()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 */
	public World getShipWorld(Ship ship) throws ModelException{
		try{
			return ship.getWorld();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getShipWorld()");
		}
	}
	
	/**
	 * @param o
	 * @return result
	 * 			|result =  o instanceof Asteroid
	 */
	public boolean isAsteroid(Object o){
		return (o instanceof Asteroid);
	}
	
	/**
	 * Get the X-position of an asteroid.
	 * @param asteroid
	 * @return asteroid.getX()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| asteroid == null
	 */
	public double getAsteroidX(Asteroid asteroid) throws ModelException{
		try{
			return asteroid.getX();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getAsteroidX()");
		}
	}
	
	/**
	 * Get the Y-position of an asteroid.
	 * @param asteroid
	 * @return asteroid.getY()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| asteroid == null
	 */
	public double getAsteroidY(Asteroid asteroid) throws ModelException{
		try{
			return asteroid.getY();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getAsteroidY()");
		}
	}
	
	/**
	 * Get the X-velocity of an asteroid.
	 * @param asteroid
	 * @return asteroid.getXVelocity()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| asteroid == null
	 */
	public double getAsteroidXVelocity(Asteroid asteroid) throws ModelException{
		try{
			return asteroid.getXVelocity();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getAsteroidXVelocity()");
		}
	}
	
	/**
	 * Get the Y-velocity of an asteroid.
	 * @param asteroid
	 * @return asteroid.getYVelocity()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| asteroid == null
	 */
	public double getAsteroidYVelocity(Asteroid asteroid) throws ModelException{
		try{
			return asteroid.getYVelocity();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getAsteroidYVelocity()");
		}
	}
	
	/**
	 * Get the radius of an asteroid.
	 * @param asteroid
	 * @return asteroid.getRadius()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| asteroid == null
	 */
	public double getAsteroidRadius(Asteroid asteroid) throws ModelException{
		try{
			return asteroid.getRadius();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getAsteroidRadius()");
		}
	}
	
	/**
	 * Get the mass of an asteroid.
	 * @param asteroid
	 * @return asteroid.getMass()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| asteroid == null
	 */
	public double getAsteroidMass(Asteroid asteroid) throws ModelException{
		try{
			return asteroid.getMass();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getAsteroidMass()");
		}
	}

	/**
	 * Get the world of an asteroid.
	 * @param asteroid
	 * @return asteroid.getWorld()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| asteroid == null
	 */
	public World getAsteroidWorld(Asteroid asteroid) throws ModelException{
		try{
			return asteroid.getWorld();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getAsteroidWorld()");
		}
	}
	
	
	public boolean isBullets(Object o){
		return isBullet(o);
	}
	/**
	 * @param o
	 * @return result
	 * 			|result =  o instanceof Bullet
	 */
	public boolean isBullet(Object o){
		return (o instanceof Bullet);
	}
	
	/**
	 * Get the X-position of a bullet.
	 * @param bullet
	 * @return bullet.getX()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| bullet == null
	 */
	public double getBulletX(Bullet bullet) throws ModelException{
		try{
			return bullet.getX();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBulletX()");
		}
	}
	
	/**
	 * Get the Y-position of a bullet.
	 * @param bullet
	 * @return bullet.getY()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| bullet == null
	 */
	public double getBulletY(Bullet bullet) throws ModelException{
		try{
			return bullet.getY();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBulletY()");
		}
	}
	
	/**
	 * Get the X-velocity of a bullet.
	 * @param bullet
	 * @return bullet.getXVelocity()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| bullet == null
	 */
	public double getBulletXVelocity(Bullet bullet) throws ModelException{
		try{
			return bullet.getXVelocity();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBulletXVelocity()");
		}
	}
	
	/**
	 * Get the Y-velocity of a bullet.
	 * @param bullet
	 * @return bullet.getYVelocity()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| bullet == null
	 */
	public double getBulletYVelocity(Bullet bullet) throws ModelException{
		try{
			return bullet.getYVelocity();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBulletYVelocity()");
		}
	}
	
	/**
	 * Get the radius of a bullet.
	 * @param bullet
	 * @return bullet.getRadius()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| bullet == null
	 */
	public double getBulletRadius(Bullet bullet) throws ModelException{
		try{
			return bullet.getRadius();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBulletRadius()");
		}
	}
	
	/**
	 * Get the mass of a bullet.
	 * @param bullet
	 * @return bullet.getMass()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| bullet == null
	 */
	public double getBulletMass(Bullet bullet) throws ModelException{
		try{
			return bullet.getMass();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBulletMass()");
		}
	}
	
	/**
	 * Get the world of a bullet.
	 * @param bullet
	 * @return bullet.getWorld()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| bullet == null
	 */
	public World getBulletWorld(Bullet bullet) throws ModelException{
		try{
			return bullet.getWorld();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBulletWorld()");
		}
	}
	
	/**
	 * Get the source of a bullet.
	 * @param bullet
	 * @return bullet.getShip()
	 * @throws ModelException
	 * 			When a null object has been given.
	 * 			| bullet == null
	 */
	public Ship getBulletSource(Bullet bullet) throws ModelException{
		try{
			return bullet.getShip();
		}
		catch (NullPointerException e){
			throw new ModelException("Null object @getBulletSource()");
		}
	}

	/**
	 *  Evolves the world.
	 *  @param world
	 *  @param dt
	 *  @param collisionListener
	 *  @effect world.evolve(dt)
	 *  @throws	ModelExcpetion
	 *  		When a null object has been given.
	 *  		| world == null
	 *  @throws	ModelException
	 *  		When the input is invalid
	 *  		| !isValidDouble(dt) || dt < 0
	 *  @throws ModelException
	 * 			When an object is in an invalid state.
	 * 			| let object be spaceObject in world
	 * 			| if(!object.isAlive())
	 */
	public void evolve(World world, double dt,
			CollisionListener collisionListener) {
		try {
			world.evolve(dt,collisionListener);
		}
		catch(IllegalArgumentException f) {
			throw new ModelException("illegal argument @ evolve()");
		}
		catch(NullPointerException f) {
			throw new ModelException("Null object @ evolve()");
		}
//		catch (IllegalStateException e) {
//			throw new ModelException("Invalid State @evolve()");
//		}
	}

	/**
	 * Fires a bullet
	 * @param ship
	 * @effect ship.fireBullet()
	 * @throws	ModelException
	 * 			When a null object has been given.
	 * 			| ship == null
	 * @throws ModelException
	 * 			When an object is in an invalid state.
	 * 			| !ship.isAlive()
	 */
	public void fireBullet(Ship ship) {
		try{
			ship.fireBullet();
		}
		catch(NullPointerException e) {
			throw new ModelException("Null Object @fireBullet()");
		}
		catch(IllegalStateException e) {
			throw new ModelException("Illegal State - ship has no world @fireBullet()");
		}
		catch(IllegalArgumentException e) {
			throw new ModelException("Illegal Argument - bullet can not spawn in world @fireBullet()");
		}
	}

	// Source
	// http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
	/**
	 * This method will convert an inputstream to a string.
	 * @param   is
	 * 		    The inutstream
	 * @param   bufferSize
	 * 		    The max size of the buffer to make the string.
	 * @return  The inputstream as a string.
	 */
	public static String slurp(final InputStream is, final int bufferSize)
	{
	  final char[] buffer = new char[bufferSize];
	  final StringBuilder out = new StringBuilder();
	  try {
	    final Reader in = new InputStreamReader(is, "UTF-8");
	    try {
	      for (;;) {
	        int rsz = in.read(buffer, 0, buffer.length);
	        if (rsz < 0)
	          break;
	        out.append(buffer, 0, rsz);
	      }
	    }
	    finally {
	      in.close();
	    }
	  }
	  catch (UnsupportedEncodingException ex) {
	  }
	  catch (IOException ex) {
	  }
	  return out.toString();
	}
	
	
}
	
	